package plus.easydo.starter.lock.configure;

import cn.hutool.core.text.CharSequenceUtil;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.BaseMasterSlaveServersConfig;
import org.redisson.config.ClusterServersConfig;
import org.redisson.config.Config;
import org.redisson.config.ReadMode;
import org.redisson.config.SentinelServersConfig;
import org.redisson.config.SingleServerConfig;
import org.redisson.config.SslProvider;
import org.redisson.config.SubscriptionMode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import plus.easydo.starter.lock.conditional.ClusterConditional;
import plus.easydo.starter.lock.conditional.SentinelConditional;
import plus.easydo.starter.lock.conditional.StandaloneConditional;
import plus.easydo.starter.lock.properties.RedissonProperties;

import javax.annotation.Resource;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;

/**
 * @author yuzhanfeng
 */
@EnableConfigurationProperties(RedissonProperties.class)
public class CustomizeRedissonAutoConfig {

    private static final String MASTER = "MASTER";

    private static final String SLAVE = "SLAVE";

    private static final String MASTER_SLAVE = "MASTER_SLAVE";

    private static final String OPENSSL = "OPENSSL";

    Logger logger = LoggerFactory.getLogger(this.getClass());

    private static final String HOST_PREFIX = "redis://";

    @Resource
    RedissonProperties properties;

    /**
     * 注册单点
     * redissonClientStandalone
     * @author yuzhanfeng
     * @return RedissonClient
     * @throws URISyntaxException u
     */
    @Bean
    @Conditional(StandaloneConditional.class)
    public RedissonClient redissonClientStandalone() throws URISyntaxException {
        logger.info("CustomizeRedissonAutoConfig ==> Enable stand-alone mode");
        Config config = new Config();
        SingleServerConfig singleServerConfig = config.useSingleServer()
                .setDatabase(properties.getRedisson().getDatabase())
                .setAddress(HOST_PREFIX + properties.getHost() + ":" + properties.getPort())
                .setConnectionPoolSize(properties.getRedisson().getConnectionPoolSize())
                .setConnectionMinimumIdleSize(properties.getRedisson().getConnectionMinimumIdleSize())
                .setDnsMonitoringInterval(properties.getRedisson().getDnsMonitoringInterval())
                .setSubscriptionConnectionPoolSize(properties.getRedisson().getSubscriptionConnectionPoolSize())
                .setSubscriptionConnectionMinimumIdleSize(properties.getRedisson().getSubscriptionConnectionMinimumIdleSize())

                .setTimeout(properties.getRedisson().getTimeout())
                .setClientName(properties.getRedisson().getClientName())
                .setKeepAlive(properties.getRedisson().isKeepAlive())
                .setTcpNoDelay(properties.getRedisson().isTcpNoDelay())
                .setRetryAttempts(properties.getRedisson().getRetryAttempts())
                .setRetryInterval(properties.getRedisson().getRetryInterval())
                .setConnectTimeout(properties.getRedisson().getConnectTimeout())
                .setSslKeystore(new URI(properties.getRedisson().getSslKeystore()))
                .setSslTruststore(new URI(properties.getRedisson().getSslTruststore()))
                .setSslKeystorePassword(properties.getRedisson().getSslKeystorePassword())
                .setIdleConnectionTimeout(properties.getRedisson().getIdleConnectionTimeout())
                .setSslTruststorePassword(properties.getRedisson().getSslTruststorePassword())
                .setPingConnectionInterval(properties.getRedisson().getPingConnectionInterval())
                .setSubscriptionsPerConnection(properties.getRedisson().getSubscriptionsPerConnection())
                .setSslEnableEndpointIdentification(properties.getRedisson().isSslEnableEndpointIdentification())
                .setSslProvider(OPENSSL.equals(properties.getRedisson().getSslProvider())?SslProvider.OPENSSL:SslProvider.JDK);

        if (CharSequenceUtil.isNotEmpty(properties.getRedisson().getPassword())) {
            singleServerConfig.setPassword(properties.getRedisson().getPassword());
        }
        return Redisson.create(config);
    }

    /**
     * 注册集群
     * redissonClientCluster
     * @author yuzhanfeng
     * @return RedissonClient
     * @throws URISyntaxException u
     */
    @Bean
    @Conditional(ClusterConditional.class)
    public RedissonClient redissonClientCluster() throws URISyntaxException {
        logger.info("CustomizeRedissonAutoConfig ==> Enable cluster mode");
        Config config = new Config();
        ClusterServersConfig clusterServersConfig = config.useClusterServers()
                .addNodeAddress(getNodes())
                .setScanInterval(properties.getRedisson().getScanInterval())

                .setTimeout(properties.getRedisson().getTimeout())
                .setClientName(properties.getRedisson().getClientName())
                .setKeepAlive(properties.getRedisson().isKeepAlive())
                .setTcpNoDelay(properties.getRedisson().isTcpNoDelay())
                .setRetryAttempts(properties.getRedisson().getRetryAttempts())
                .setRetryInterval(properties.getRedisson().getRetryInterval())
                .setConnectTimeout(properties.getRedisson().getConnectTimeout())
                .setSslKeystore(new URI(properties.getRedisson().getSslKeystore()))
                .setSslTruststore(new URI(properties.getRedisson().getSslTruststore()))
                .setSslKeystorePassword(properties.getRedisson().getSslKeystorePassword())
                .setIdleConnectionTimeout(properties.getRedisson().getIdleConnectionTimeout())
                .setSslTruststorePassword(properties.getRedisson().getSslTruststorePassword())
                .setPingConnectionInterval(properties.getRedisson().getPingConnectionInterval())
                .setSubscriptionsPerConnection(properties.getRedisson().getSubscriptionsPerConnection())
                .setSslEnableEndpointIdentification(properties.getRedisson().isSslEnableEndpointIdentification())
                .setSslProvider(OPENSSL.equals(properties.getRedisson().getSslProvider())?SslProvider.OPENSSL:SslProvider.JDK);

        setBaseMasterSlaveServersConfig(clusterServersConfig);
        setReadMode(clusterServersConfig);
        setPassword(clusterServersConfig);
        return Redisson.create(config);
    }



    /**
     * 注册哨兵模式
     * redissonClientCluster
     * @author yuzhanfeng
     * @return RedissonClient
     * @throws URISyntaxException u
     */
    @Bean
    @Conditional(SentinelConditional.class)
    public RedissonClient redissonClientSentinel() throws URISyntaxException {
        logger.info("CustomizeRedissonAutoConfig ==> Enable Sentinel mode");
        Config config = new Config();
        SentinelServersConfig sentinelServersConfig = config.useSentinelServers()
                .addSentinelAddress(getNodes())
                .setDatabase(properties.getRedisson().getDatabase())
                .setMasterName(properties.getRedisson().getMasterName())
                .setScanInterval(properties.getRedisson().getScanInterval())

                .setTimeout(properties.getRedisson().getTimeout())
                .setClientName(properties.getRedisson().getClientName())
                .setKeepAlive(properties.getRedisson().isKeepAlive())
                .setTcpNoDelay(properties.getRedisson().isTcpNoDelay())
                .setRetryAttempts(properties.getRedisson().getRetryAttempts())
                .setRetryInterval(properties.getRedisson().getRetryInterval())
                .setConnectTimeout(properties.getRedisson().getConnectTimeout())
                .setSslKeystore(new URI(properties.getRedisson().getSslKeystore()))
                .setSslTruststore(new URI(properties.getRedisson().getSslTruststore()))
                .setSslKeystorePassword(properties.getRedisson().getSslKeystorePassword())
                .setIdleConnectionTimeout(properties.getRedisson().getIdleConnectionTimeout())
                .setSslTruststorePassword(properties.getRedisson().getSslTruststorePassword())
                .setPingConnectionInterval(properties.getRedisson().getPingConnectionInterval())
                .setSubscriptionsPerConnection(properties.getRedisson().getSubscriptionsPerConnection())
                .setSslEnableEndpointIdentification(properties.getRedisson().isSslEnableEndpointIdentification())
                .setSslProvider(OPENSSL.equals(properties.getRedisson().getSslProvider())?SslProvider.OPENSSL:SslProvider.JDK);

        setBaseMasterSlaveServersConfig(sentinelServersConfig);
        setReadMode(sentinelServersConfig);
        setPassword(sentinelServersConfig);
        return Redisson.create(config);
    }

    private <T extends BaseMasterSlaveServersConfig<T>> void setBaseMasterSlaveServersConfig(BaseMasterSlaveServersConfig<T> baseMasterSlaveServersConfig) {
        baseMasterSlaveServersConfig
                .setMasterConnectionPoolSize(properties.getRedisson().getMasterConnectionPoolSize())
                .setSlaveConnectionPoolSize(properties.getRedisson().getSlaveConnectionPoolSize())
                .setSubscriptionConnectionPoolSize(properties.getRedisson().getSubscriptionConnectionPoolSize())
                .setMasterConnectionMinimumIdleSize(properties.getRedisson().getMasterConnectionMinimumIdleSize())
                .setSlaveConnectionMinimumIdleSize(properties.getRedisson().getSlaveConnectionMinimumIdleSize())
                .setSubscriptionConnectionMinimumIdleSize(properties.getRedisson().getSubscriptionConnectionMinimumIdleSize())
                .setSubscriptionMode(SLAVE.equals(properties.getRedisson().getSubscriptionMode()) ? SubscriptionMode.SLAVE : SubscriptionMode.MASTER)
                .setDnsMonitoringInterval(properties.getRedisson().getDnsMonitoringInterval())
                .setFailedSlaveCheckInterval(properties.getRedisson().getFailedSlaveCheckInterval())
                .setFailedSlaveReconnectionInterval(properties.getRedisson().getFailedSlaveReconnectionInterval());
    }

    private <T extends BaseMasterSlaveServersConfig<T>> void setReadMode(BaseMasterSlaveServersConfig<T> baseMasterSlaveServersConfig) {
        String readMode = properties.getRedisson().getReadMode();
        if (CharSequenceUtil.isNotEmpty(readMode)) {
            if (SLAVE.equals(readMode)) {
                baseMasterSlaveServersConfig.setReadMode(ReadMode.MASTER);
            } else if (MASTER.equals(readMode)) {
                baseMasterSlaveServersConfig.setReadMode(ReadMode.MASTER);
            } else if (MASTER_SLAVE.equals(readMode)) {
                baseMasterSlaveServersConfig.setReadMode(ReadMode.MASTER_SLAVE);
            }
        }
    }

    private <T extends BaseMasterSlaveServersConfig<T>> void setPassword(BaseMasterSlaveServersConfig<T> baseMasterSlaveServersConfig) {
        if (CharSequenceUtil.isNotEmpty(properties.getRedisson().getPassword())) {
            baseMasterSlaveServersConfig.setPassword(properties.getRedisson().getPassword());
        }
    }

    /**
     * 功能描述
     *
     * @return java.lang.String[]
     * @author laoyu
     */
    private String[] getNodes(){
        List<String> nodes = properties.getRedisson().getNodes();
       return nodes.stream().map(node-> node.startsWith(HOST_PREFIX)? node: HOST_PREFIX +node).toArray(String[]::new);
    }


}
